/*
 * Script that contains the pseudo-class definition
 * for a DecisionLines event
 * 
 * Author: Chris Casola
 */

(function() {

	/**
	 * Represents a DecisionLines event
	 */
	function Event(numChoices, numRounds) {
		
		/** Adjacency constant */
		this.ADJ_LIMIT = 7;
		
		/** The number of choices in the event */
		this.numChoices = numChoices;
		
		/** The number of rounds in the event */
		this.numRounds = numRounds;
		
		/** The question being considered */
		this.question = "";
		
		/** The Choices in the event */
		this.choices = new Array();
		
		/** State of the event
		 * 0 - waiting for choices
		 * 1 - choices are full
		 * 2 - rounds have begun
		 * 3 - finished
		 */
		this.state = 0;
		
		/** The player's position */
		this.playerPosition = 0;
		
		/** Current player */
		this.currentPlayer = 0;
		
		/** Current round */
		this.currentRound = 1;
		
		/** The type of the event, open or closed */
		this.eventType = "";
		
		/** The name of the person creating the event */
		this.username = "";
		
		/** Is it this player's turn */
		this.myTurn = false;
		
		constructChoices(this); // Construct each of the Choice objects in the choices array
		
		/**
		 * Adds a new Edge to the Event
		 * @param hPos the horizontal position of the left Choice
		 * @param vPos the vertical position of the Edge
		 */
		this.addEdge = function(hPos, vPos) {
			if (this.state > 2) {
				return false;
			}
			if ((hPos < 0) || (hPos > this.numChoices - 2)) {
				throw "Not a valid Choice index";
			}
			if (this.checkAdjConst(vPos, hPos) && this.checkAdjConst(vPos, hPos + 1)) {
				this.choices[hPos].addEdge(vPos, "right");
				this.choices[hPos + 1].addEdge(vPos, "left");
				this.status = "Edge added between " + this.choices[hPos].label + " and " + this.choices[hPos+1].label + ".";
				this.nextTurn();
				if (this.currentRound > this.numRounds) {
					this.status = "Event over!";
					this.state = 3;
				}
				return true;
			}
			return false;
		};
		
		/**
		 * Set the label of the ith Choice
		 */
		this.setChoiceLabel = function(label, choiceIndex) {
			if (this.validateLabel(label) == false) {
				return;
			}
			else {
				this.choices[choiceIndex].label = label;
				if (this.checkChoicesFull()) {
					this.status = "Start adding edges.";
					this.state = 1;
				}
				else {
					this.status = "You entered the choice '" + label + "'";
				}
			}
		};
		
		/**
		 * Move the event into the adding edges stage
		 */
		this.beginRounds = function() {
			this.status = "Round: " + this.currentRound + "  Player: " + this.currentPlayer;
			this.state = 2;
		};
		
		/**
		 * Returns an array of Strings containing the Choice labels
		 * in this Event in prioritized order
		 */
		this.getPrioritizedOrder = function() {
			var result = new Array();
			for (var i = 0; i < this.numChoices; i++) {
				result[this.followPath(i)] = this.choices[i].label; 
			}
			return result;
		};
		
		/**
		 * Traverses a route across this Event's edges starting with
		 * the Choice indicated and returning the position of this
		 * Choice in the prioritized order.
		 */
		this.followPath = function(choiceIndex) {
			var lastEdgePos = 0;
			while (this.choices[choiceIndex].getNextEdge(lastEdgePos) != null) {
				lastEdgePos = this.choices[choiceIndex].getNextEdge(lastEdgePos).vPos;
				if (this.choices[choiceIndex].side == "right") {
					choiceIndex++;
				}
				else {
					choiceIndex--;
				}
			}
			return choiceIndex;
		};
		
		/**
		 * Increments the current player and current round and
		 * sets the status accordingly
		 */
		this.nextTurn = function() {
			if (this.currentPlayer == (this.numChoices - 1)) {
				this.currentPlayer = 0;
				this.currentRound += 1;
			}
			else {
				this.currentPlayer += 1;
			}
			this.status = "Round: " + this.currentRound + "  Player: " + this.currentPlayer;
		};
		
		/**
		 * Validates adjacency constraint for all Edges on the right side of the given choice
		 */
		this.checkAdjConst = function (vPos, choiceIndex) {
			if (choiceIndex > this.numChoices - 2) {
				return true;
			}
			if ((this.choices[choiceIndex].getPrevEdge(vPos + 1) != null) && (vPos - this.choices[choiceIndex].getPrevEdge(vPos + 1).vPos) <= this.ADJ_LIMIT) {
				this.status = "Invalid edge: the requested edge would be too close to neighboring edges.";
				return false;
			}
			else if ((this.choices[choiceIndex].getNextEdge(vPos - 1) != null) && (this.choices[choiceIndex].getNextEdge(vPos - 1).vPos - vPos) <= this.ADJ_LIMIT) {
				this.status = "Invalid edge: the requested edge would be too close to neighboring edges.";
				return false;
			}
			return true;
		};
		
		/**
		 * Check the given label to ensure it is not blank
		 * or already used for another Choice.
		 */
		this.validateLabel = function(label) {
			if (label.length == 0) {
				status = "Invalid choice: you cannot enter a blank choice!";
				return false;
			}
			else if (!this.checkUniqueChoice(label)) {
				status = "Invalid choice: each choice must be unique!";
				return false;
			}
			return true;
		};
		
		/**
		 * Check if the given Choice label is unique
		 */
		this.checkUniqueChoice = function(label) {
			for (var i=0; i < this.choices.length; i++) {
				if (this.choices[i].label == label) {
					return false;
				}
			}
			return true;
		};
		
		/**
		 * Checks if all Choice labels have been set
		 */
		this.checkChoicesFull = function() {
			for (var i=0; i < this.choices.length; i++) {
				if (this.choices[i].label == "") {
					return false;
				}
			}
			return true;
		};
		
		/**
		 * Constructs each of the Choices in this Event's list of Choices
		 */
		function constructChoices(self) {
			for (var i = 0; i < self.numChoices; i++) {
				self.choices[i] = new Choice(i);
			}
		};
		
		/**
		 * Converts the Event into a json string
		 */
		this.toJSON = function() {
			return {
				__XMLclass : "createRequest",
				type : this.eventType,
				question : this.question,
				numChoices : this.numChoices,
				numRounds : this.numRounds,
				__XMLchildren : this.generateChildren()
			};
		}
		
		/**
		 * Adds the choices in the event into an array specially formed
		 * for sending requests to the server
		 */
		this.generateChildren = function() {
			var children = new Array();			
			for (var i=0; i < this.choices.length; i++) {
				if (this.choices[i].label.length > 0) {
					children.push({
						__XMLclass : "choice",
						value : this.choices[i].label,
						index : i.toString()
					});
				}
			}
			children.push({
				__XMLclass : "user",
				name : this.username
			});
			return children;
		}
		
		/**
		 * Close the event
		 */
		this.closeEvent = function() {
			var blankChoices = this.blankChoices();
			
			while(blankChoices.length > 0) {
				this.choices.splice(blankChoices.pop(), 1);
			}
			this.numChoices = this.choices.length;
			this.eventType = "closed";
		}
		
		/**
		 * Return the number of choices that are currently set
		 */
		this.blankChoices = function () {
			var retVal = [];
			for (var i=0; i<this.choices.length; i++) {
				if (this.choices[i].label == "") {
					retVal.push(i);
				}
			}
			return retVal;
		}
	}
	window.Event = Event;
}).call();
